package Test;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import Dispatcher.GroupDispatcher;
import Dispatcher.UpdateThread;
import View.BuildingGUI;


public class OneElevatorStressTests {
	
	private static GroupDispatcher dispatcher;
	private static BuildingGUI gui;
	
	//create the group dispatcher
	@BeforeClass
	public static void setUp() throws Exception {
		dispatcher = new GroupDispatcher();
		gui = new BuildingGUI(dispatcher);
		gui.setVisible(true);
		UpdateThread ut = new UpdateThread(dispatcher);
		ut.start();

	}
	
	//after all tests have run, close sockets and other clean-up
	@AfterClass
	public static void tearDown() throws Exception {
		dispatcher.finalize();
	}
	
	//Get the minimum wait time for the person waiting for the elevator on a floor
	//the minimum wait time is when the person is when the difference between the current
	//elevator floor and the hall call request floor is one
	/*@Test
	public void testMinimumResponseTime() throws InterruptedException {
		Thread.sleep(5000);
		
		long[] durations = new long[9];
		Thread.sleep(5000);
		for(int i=2; i < 11; i++){
			long start = System.currentTimeMillis();
			dispatcher.processHallCall(i, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != i){
				Thread.sleep(1);
			}
			durations[i-2] = System.currentTimeMillis() - start;
			Thread.sleep(5000);
		}
		
		this.writeToFile("One Elevator Tests\n");
		
		double average = calculateAverage(durations);
		double stdDev = calculateStdDev(durations, average);
		double[] durationsInSec = durationsInSeconds(durations);
		
		for (int i = 0; i < durationsInSec.length; i++) {
			this.writeToFile(durationsInSec[i] + "\n");
		}
		
		//-------TO FILE--------------
		this.writeToFile("Minimum wait time: " + average 
						+ "s " +"with standard deviation: " + stdDev + "s\n\n");
		//-------TO FILE--------------
	}*/
	
	/*@Test
	public void testBestCaseMaximumResponseTime() throws InterruptedException {
		Thread.sleep(5000);
		
		long[] durations = new long[10];
		int j = 0;
		for (int i = 0; i < 5; i++) {
			Thread.sleep(5000);
			long start = System.currentTimeMillis();
			dispatcher.processHallCall(10, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 10){
				Thread.sleep(1);
			}
			durations[j] = System.currentTimeMillis() - start;
			j++;
			
			Thread.sleep(5000);
			start = System.currentTimeMillis();
			dispatcher.processHallCall(1, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
			durations[j] = System.currentTimeMillis() - start;
			j++;
			/*Thread.sleep(5000);
			long start = System.currentTimeMillis();
			dispatcher.processHallCall(1, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
			durations[j] = System.currentTimeMillis() - start;
			j++;
			
			Thread.sleep(5000);
			start = System.currentTimeMillis();
			dispatcher.processHallCall(10, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
			durations[j] = System.currentTimeMillis() - start;
			j++;
		}
		
		double average = calculateAverage(durations);
		double stdDev = calculateStdDev(durations, average);
		double[] durationsInSec = durationsInSeconds(durations);
		
		for (int i = 0; i < durationsInSec.length; i++) {
			this.writeToFile(durationsInSec[i] + "\n");
		}
		
		//-------TO FILE--------------
		this.writeToFile("Maximum wait time\n" +
						"This is when the difference between the current floor of elevator " +
						"and hall call request floor is maximum\n" +
						"Best Case: hall call at 10, elevator currently at 1, no hall calls in between\n" + 
						"Best Case Wait Time: " + average + "s " +" with standard deviation: " + stdDev + "s\n\n");
		
		//-------TO FILE--------------
		
	}*/
	
	/*@Test
	public void testWorstCaseMaximumResponseTime() throws InterruptedException {
		Thread.sleep(5000);
		
		long[] durations = new long[10];
		for (int i = 0; i < 10; i++) {
			Thread.sleep(2000);
			long start = System.currentTimeMillis();
			for (int j = 2; j < 11; j++) {
				dispatcher.processHallCall(j, 1);
				Thread.sleep(100);
			}
			while(dispatcher.status.get(1).getCurrentFloor() != 10){
				Thread.sleep(1);
			}
			durations[i] = System.currentTimeMillis() - start;
			
			dispatcher.processHallCall(1, 1);
			Thread.sleep(10000);
			
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
		}
		
		double average = calculateAverage(durations);
		double stdDev = calculateStdDev(durations, average);
		double[] durationsInSec = durationsInSeconds(durations);
		
		for (int i = 0; i < durationsInSec.length; i++) {
			this.writeToFile(durationsInSec[i] + "\n");
		}
		
		//-------TO FILE--------------
		this.writeToFile("Worst Case: hall call at 10, elevator currently at 1, hall calls on all floors between\n" + 
						"Worst Case Wait Time: " + average + "s " +" with standard deviation: " + stdDev + "s\n\n");
		//-------TO FILE--------------
	}*/
	
	
	@Test
	public void testAverageCaseMaximumResponseTime() throws InterruptedException {
		Thread.sleep(5000);
		
		long[] durations = new long[10];
		for (int i = 0; i < 2; i++) {
			Thread.sleep(2000);
			long start = System.currentTimeMillis();
			for (int j = 2; j < 6; j++) {
				dispatcher.processHallCall(j, 1);
				Thread.sleep(100);
			}
			while(dispatcher.status.get(1).getCurrentFloor() != 5){
				Thread.sleep(1);
			}
			durations[i] = System.currentTimeMillis() - start;
			
			dispatcher.processHallCall(1, 1);
			Thread.sleep(10000);
			
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
		}
		
		double average = calculateAverage(durations);
		double stdDev = calculateStdDev(durations, average);
		double[] durationsInSec = durationsInSeconds(durations);
		
		for (int i = 0; i < durationsInSec.length; i++) {
			this.writeToFile(durationsInSec[i] + "\n");
		}
		
		//-------TO FILE--------------
		this.writeToFile("Average Case: hall call at 10, elevator currently at 1, hall calls on half the floors between\n" + 
						"Average Case Wait Time: " + average + "s " + " with standard deviation: " + stdDev + "s\n\n");
		//-------TO FILE--------------
	}
	
	//people getting back home - elevator almost filled to maximum
	//scenario
	//elevators stops at floors 2, 4, 6, 7, 10
	//then back to floor 1 to collect more people
	//then stops at floors 2 and 4
	//back to floor 1
	//again back up to 3 and 7
	//back to floor 1
	//then back up to 5
	//finally back to floor 1
	@Test
	public void testAfternoonRushWorstCaseResponseTime() throws InterruptedException {
		Thread.sleep(5000);
		long[] durations = new long[5];
		
		for (int i = 0; i < 5; i++) {
			Thread.sleep(5000);
			long start = System.currentTimeMillis();
			//stops at floor 2
			dispatcher.processHallCall(2, 1);
			Thread.sleep(100);
			//stops at floor 4
			dispatcher.processHallCall(4, 1);
			Thread.sleep(100);
			//stops at floor 6
			dispatcher.processHallCall(6, 1);
			Thread.sleep(100);
			//stops at floor 7
			dispatcher.processHallCall(7, 1);
			Thread.sleep(100);
			//stops at floor 10
			dispatcher.processHallCall(10, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 10){
				Thread.sleep(1);
			}
			
			//back to floor 1
			dispatcher.processHallCall(1, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
			
			//stops at floor 2
			dispatcher.processHallCall(2, 1);
			Thread.sleep(100);
			//stops at floor 4
			dispatcher.processHallCall(4, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 4){
				Thread.sleep(1);
			}
			
			//back to floor 1
			dispatcher.processHallCall(1, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
			
			//stops at floor 3
			dispatcher.processHallCall(3, 1);
			Thread.sleep(100);
			//stops at floor 7
			dispatcher.processHallCall(7, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 7){
				Thread.sleep(1);
			}
			
			//back to floor 1
			dispatcher.processHallCall(1, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
			
			//stops at floor 5
			dispatcher.processHallCall(5, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 5){
				Thread.sleep(1);
			}
			
			//finally, back to floor 1
			dispatcher.processHallCall(1, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
			
			durations[i] = System.currentTimeMillis() - start;
		}
		
		double average = calculateAverage(durations);
		double stdDev = calculateStdDev(durations, average);
		double[] durationsInSec = durationsInSeconds(durations);
		
		for (int i = 0; i < durationsInSec.length; i++) {
			this.writeToFile(durationsInSec[i] + "\n");
		}
		
		this.writeToFile("Afternoon Rush Scenario Average Time: " + average + "s " +
					"with standard deviation: " + stdDev + "s\n\n");
	}
	
	//people leaving in the morning - elevator almost filled to maximum
	//scenario
	//elevator at floor 1
	//request at floor 5 to go to floor 1
	//elevator at floor 1
	//go back up to floors 10, 8, 6, 3 to go to floor 1
	//elevator at floor 1
	//back up to floors 3 and 7
	//elevator at floor 1
	//back up to floors 6 and 4
	//elevator at floor 1
	@Test
	public void testMorningRushWorstCaseResponseTime() throws InterruptedException {
		Thread.sleep(5000);
		long[] durations = new long[5];
		
		for(int i = 0; i<5; i++){
			Thread.sleep(5000);
			long start = System.currentTimeMillis();
			
			//request at floor 5
			dispatcher.processHallCall(5, -1);
			while(dispatcher.status.get(1).getCurrentFloor() != 5){
				Thread.sleep(1);
			}
			
			//stops at floor 1
			dispatcher.processHallCall(1, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
			
			//request at floor 10
			dispatcher.processHallCall(10, -1);
			Thread.sleep(100);
			//request at floor 8
			dispatcher.processHallCall(8, -1);
			Thread.sleep(100);
			//request at floor 6
			dispatcher.processHallCall(6, -1);
			Thread.sleep(100);
			//request at floor 3
			dispatcher.processHallCall(3, -1);
			while(dispatcher.status.get(1).getCurrentFloor() != 3){
				Thread.sleep(1);
			}
			
			//stops at floor 1
			dispatcher.processHallCall(1, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
			
			//request at floor 7
			dispatcher.processHallCall(7, -1);
			Thread.sleep(100);
			//request at floor 3
			dispatcher.processHallCall(3, -1);
			while(dispatcher.status.get(1).getCurrentFloor() != 3){
				Thread.sleep(1);
			}
			
			//stops at floor 1
			dispatcher.processHallCall(1, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
			
			//request at floor 6
			dispatcher.processHallCall(6, -1);
			Thread.sleep(100);
			//request at floor 4
			dispatcher.processHallCall(4, -1);
			while(dispatcher.status.get(1).getCurrentFloor() != 3){
				Thread.sleep(1);
			}
			
			//stops at floor 1
			dispatcher.processHallCall(1, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1){
				Thread.sleep(1);
			}
			
			durations[i] = System.currentTimeMillis() - start;
		}
		
		double average = calculateAverage(durations);
		double stdDev = calculateStdDev(durations, average);
		double[] durationsInSec = durationsInSeconds(durations);
		
		for (int i = 0; i < durationsInSec.length; i++) {
			this.writeToFile(durationsInSec[i] + "\n");
		}
		
		this.writeToFile("Morning Rush Scenario Average Time: " + average + "s " +
					"with standard deviation: " + stdDev + "s\n\n");
	}
	
	//--------------------Helpers---------------------------
	private void writeToFile(String s){
		File file = new File("./documents/Milestone-4/StressTests.txt");
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(file, true));
			writer.write(s);
			writer.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public double calculateAverage(long[] durations) {
		long sum = 0;
		for (int i = 0; i < durations.length; i++) {
			sum += durations[i];
		}
		return (sum / durations.length)/1000.0;
	}
	
	public double calculateStdDev(long[] durations, double average) {
		double avgInMilli = average*1000;
		long squaredDiff = 0;
		for (int i = 0; i < durations.length; i++) {
			squaredDiff += Math.pow(durations[i] - avgInMilli, 2);
		}
		return Math.sqrt(squaredDiff / durations.length)/1000;
	}
	
	public double[] durationsInSeconds(long[] durations) {
		double[] result = new double[durations.length];
		for (int i = 0; i < durations.length; i++) {
			result[i] = durations[i]/1000.0;
		}
		return result;
	}
}
